package jmine.tec.test.entities.dataType;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import org.hibernate.usertype.UserType;

import bancosys.tec.utils.date.Date;

/**
 * Type para mapear um Date.
 * 
 * @created Dec 26, 2006
 * @author Gustavo Almeida
 */
public class EpochType implements UserType {

    private static final int[] SQL_TYPES = { Types.NUMERIC };

    /**
     * Retorna os códigos dos tipos do SQL para as colunas mapeadas por esse UserType. Os códigos estão definidos em <tt>java.sql.Types</tt>
     * .
     * 
     * @see java.sql.Types.
     * @return os typecodes.
     */
    public int[] sqlTypes() {
        return SQL_TYPES;
    }

    /**
     * A classe retornada por <tt>nullSafeGet()</tt>.
     * 
     * @return a classe retornada por <tt>nullSafeGet()</tt>.
     */
    public Class<?> returnedClass() {
        return Date.class;
    }

    /**
     * Compara duas instancias da classe mapeada por esse tipo para igualdade de persistência.
     * 
     * @param x um objeto.
     * @param y um objeto.
     * @return <code>true</code> se forem iguais, <code>false</code> caso contrário.
     */
    public boolean equals(Object x, Object y) {
        if (x != null) {
            return x.equals(y);
        } else {
            return y == null;
        }

    }

    /**
     * Recupera uma instancia da classe mapeada, de um resultset JDBC.
     * 
     * @param rs um resultset JDBC.
     * @param names os nomes das colunas.
     * @param owner a entidade.
     * @return uma instancia da classe mapeada.
     * @throws SQLException caso ocorra algum erro.
     */
    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws SQLException {
        Long sqlDate = rs.getLong(names[0]);
        if (sqlDate == null) {
            return null;
        } else {
            return new Date(sqlDate);
        }
    }

    /**
     * Escreve uma instancia da classe mapeada no statement JDBC preparado. Um tipo com mais de uma coluna será escrito em parametros
     * inciando em <tt>index</tt>.
     * 
     * @param st um statement JDBC preparado.
     * @param value o objeto a escrever.
     * @param index o indice do parametro do statement JDBC.
     * @throws SQLException caso ocorra algum erro.
     */
    public void nullSafeSet(PreparedStatement st, Object value, int index) throws SQLException {
        if (value == null) {
            st.setNull(index, Types.NUMERIC);
        } else {
            st.setLong(index, ((java.sql.Date) value).getTime());
        }
    }

    /**
     * Retorna uma copia do estado persistente.
     * 
     * @param value o objeto a ser clonado.
     * @return uma copia
     */
    public Object deepCopy(Object value) {
        if (value == null) {
            return null;
        }
        return new Date((Date) value);
    }

    /**
     * Os objetos desse tipo são mutáveis?
     * 
     * @return <code>true</code> caso afirmativo, <code>false</code> caso contrário.
     */
    public boolean isMutable() {
        return true;
    }

    /**
     * Devovle o hashcode para a instância, consistente com a igualdade de persistência.
     * 
     * @param x um objeto.
     * @return o hashcode.
     */
    public int hashCode(Object x) {
        if (x == null) {
            return 0;
        }
        return x.hashCode();
    }

    /**
     * Transforma um objeto em sua representação em cache.
     * 
     * @param value o objeto a ir para cache.
     * @return uma representação serializável do objeto.
     */
    public Serializable disassemble(Object value) {
        if (value == null) {
            return null;
        } else {
            return (Serializable) this.deepCopy(value);
        }
    }

    /**
     * Recontroi um objeto de sua representação em cache.
     * 
     * @param cached o objeto a ir para cache.
     * @param owner o dono do objeto em cache.
     * @return um objeto recontruido a partir se sua representação em cache.
     */
    public Object assemble(Serializable cached, Object owner) {
        if (cached == null) {
            return null;
        } else {
            return this.deepCopy(cached);
        }
    }

    /**
     * Duranto o merge, substitui o valor existente (target) com o novo valor (original).
     * 
     * @param original o valor da entidade detached.
     * @param target o valor da entidade gerenciada.
     * @param owner o dono da entidade.
     * @return the value to be merged
     */
    public Object replace(Object original, Object target, Object owner) {
        if (original != null && target != null) {
            ((Date) target).setTime(((Date) original).getTime());
        }
        return original;
    }

}
